class Compiler {
    constructor(vm) {
        //使用变量存储实例
        this.vm = vm;
        this.el = vm.$el;
        this.compiler(this.el)
    }
    //用来编译模版，处理文本和元素节点
    compiler(el){
        let childNodes = el.childNodes;
        Array.from(childNodes).forEach( node => {
            if(this.isTextNode(node)){
                //处理文本节点
                this.compilerText(node)
            }
            //处理元素节点
            if(this.isElementNode(node)){
                this.compilerElement(node)
            }
            //判断node下是否有子节点，若有则继续递归遍历
            if(node.childNodes && node.childNodes.length){
                this.compiler(node)
            }
        })
    }
    //处理元素节点
    compilerElement(node){
        Array.from(node.attributes).forEach(attr => {
            //获取节点name
            let attrName = attr.name
            if(this.isDirective(attrName)){
                //attrName就是v-text的text或者其他-后边的内容
                attrName = attrName.split('-')[1];
                //key就是指令的值v-text="name";就是name
                let key = attr.value;
                let [type,directName] = attrName.split(':');
                this.update(node,key,attrName);
                //给添加on/bind指令
                if(directName){
                    this[type](node,directName,key)
                }
                //移除掉指令
                node.removeAttribute('v-'+attrName);
            }
        })
    }
    //处理文本节点，处理插值表达式
    compilerText(node){
        //正则匹配{{}}内的内容
        let reg = /\{\{(.+?)\}\}/;
        //拿到node的插值的整个内容
        let val = node.textContent;
        if(reg.test(val)){
            //匹配到{}中的内容可能有空格，需要trim下，去掉空格
            let key = RegExp.$1.trim();
            //替换textContent的内容为实例下的对应的值
            node.textContent = val.replace(reg,this.vm[key])
            new Watcher(this.vm,key,(newVal) => {
                node.textContent = newVal
            })
        }

    }
    update(node,key,name){
        if(key==='true'||key==='false'){
            //这里传入的值可能是字符串的true/false，做个处理让转成boolean，然后传给方法来调用(存在瑕疵)
            let updateFn = this.showUpdater;
            updateFn && updateFn.call(this,node,eval(key),key);
        }else {
            let updateFn = this[name+'Updater'];
            //this.vm[key]就是找到对应的值，此刻是name，所以就找
            updateFn && updateFn.call(this,node,this.vm[key],key)
        }
    }
    //来一个v-bind
    bind(node,directName,key){
        node[directName] = this.vm[key];
        new Watcher(this.vm,key,(newVal) => {
            node[directName] = this.vm[newVal];
        })
        
    }
    //v-if
    // ifUpdater(node,val,key){
    //     let parent = node.parentNode;
    //     for(let i=0;i<parent.childNodes.length;i++){
    //         parent.childNodes.index = i;
    //     };
    //     //整成注释
    //     // let comment = document.createComment(' ')
    //     // parent.removeChild(comment,node);
    //     if(!val){
    //         parent.removeChild(node);
    //     }
    //     new Watcher(this.vm,key,(newVal) => {
    //         if(!newVal){
    //             parent.removeChild(node);
    //         }
    //     })
    // }
    //v-on
    on(node,directName,key){
        // node.addEventListener(directName,this.vm.$options.methods[key]);
        // 获取实例对象中的methods中的方法
        const fn = this.vm.$options.methods && vm.$options.methods[key];
        // 绑定事件
        node.addEventListener(eventName, function (ev) {
            fn.call(vm, ev)// 改变fn函数内部的this,并传递事件对象event
        }, false)
    }
    //尝试写个v-show
    showUpdater(node,val,key){
        node.style.display = val ? 'block':'none';
        new Watcher(this.vm,key,(newVal) => {
            node.style.display = newVal ? 'block':'none';
        })
    }
    //v-html，改变node的innerHTML
    htmlUpdater(node,val,key){
        node.innerHTML = val;
        new Watcher(this.vm,key,(newVal) => {
            node.innerHTML = newVal
        })
    }
    // 处理v-text的方法
    textUpdater(node,val,key){
        node.textContent = val;
        new Watcher(this.vm,key,(newVal) => {
            node.textContent = newVal
        })
    }
    // 处理v-model的方法
    modelUpdater(node,val,key){
        node.value = val;
        new Watcher(this.vm,key,(newVal) => {
            node.value = newVal;
        });
        node.addEventListener('input',()=>{
            this.vm[key] = node.value;
        })
    }
    isTextNode (node){
        //文本节点的nodetype是3
        return node.nodeType === 3
    }
    isElementNode (node){
        //元素节点nodetype是1
        return node.nodeType === 1
    }
    isDirective(attrName){
        //判断是否是指令
        return attrName.startsWith('v-') || attrName.startsWith('@') || attrName.startsWith(':')
    }
}